package com.example.myyunpan.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.myyunpan.component.RedisComponent;
import com.example.myyunpan.entity.config.AppConfig;
import com.example.myyunpan.entity.constants.Constants;
import com.example.myyunpan.entity.domain.UserInfo;
import com.example.myyunpan.entity.dto.QQInfoDto;
import com.example.myyunpan.entity.dto.SessionWebUserDto;
import com.example.myyunpan.entity.dto.SysSettingsDto;
import com.example.myyunpan.entity.dto.UserSpaceDto;
import com.example.myyunpan.entity.enums.PageSize;
import com.example.myyunpan.entity.enums.UserStatusEnum;
import com.example.myyunpan.entity.query.SimplePage;
import com.example.myyunpan.entity.query.UserInfoQuery;
import com.example.myyunpan.entity.vo.PaginationResultVO;
import com.example.myyunpan.exception.BusinessException;
import com.example.myyunpan.mapper.FileInfoMapper;
import com.example.myyunpan.mapper.UserInfoMapper;
import com.example.myyunpan.service.EmailCodeService;
import com.example.myyunpan.service.FileInfoService;
import com.example.myyunpan.service.UserInfoService;
import com.example.myyunpan.utils.JsonUtils;
import com.example.myyunpan.utils.OKHttpUtils;
import com.example.myyunpan.utils.StringTools;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
* @author 111
* @description 针对表【user_info(用户信息)】的数据库操作Service实现
* @createDate 2024-10-23 11:41:27
*/
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private EmailCodeService emailCodeService;

    @Autowired
    private RedisComponent redisComponent;

    @Autowired
    private AppConfig appConfig;

    @Autowired
    private FileInfoMapper fileInfoMapper;

    @Autowired
    private FileInfoService fileInfoService;

    private static final Logger logger = LoggerFactory.getLogger(UserInfoServiceImpl.class);

    @Override
    @Transactional
    public void register(String email, String nickName, String password, String emailCode) {
        UserInfo userInfo = userInfoMapper.selectByEmailUserInfo(email);
        if(userInfo != null){
            throw new RuntimeException("邮箱已经存在!");
        }
        UserInfo userInfoName =userInfoMapper.selectByNickNameUserInfo(nickName);
        if(userInfoName != null){
            throw new RuntimeException("昵称已经存在!");
        }
        emailCodeService.checkEmailCode(email, emailCode);

//        验证完成之后,对用户信息进行注册插入
        String randomNumber = StringTools.getRandomNumber(Constants.LENGTH_10);
        userInfo=new UserInfo();
        userInfo.setUserId(randomNumber);
        userInfo.setNickName(nickName);
        userInfo.setPassword(StringTools.encodeByMD5(password));
        userInfo.setEmail(email);
        userInfo.setJoinTime(new Date());
        userInfo.setStatus(UserStatusEnum.ENABLE.getStatus());
        userInfo.setUseSpace(0L);
        SysSettingsDto sysSettings = redisComponent.getSysSettings();
        userInfo.setTotalSpace(sysSettings.getUserInitUseSpace() * Constants.MB);
        userInfoMapper.insert(userInfo);
    }

    @Override
    public SessionWebUserDto login(String email, String password) {
        UserInfo userInfo = userInfoMapper.selectByEmailUserInfo(email);
        if(userInfo == null || !userInfo.getPassword().equals(password)){
            throw new RuntimeException("账户或者密码错误!");
        }
        if(UserStatusEnum.DISABLE.getStatus().equals(userInfo.getStatus())){
            throw new RuntimeException("账户被禁用!");
        }
        UserInfo updateInfo = new UserInfo();
        updateInfo.setLastLoginTime(new Date());
        userInfoMapper.updateByUserId(updateInfo, userInfo.getUserId());
        SessionWebUserDto sessionWebUserDto = new SessionWebUserDto();
        sessionWebUserDto.setUserId(userInfo.getUserId());
        sessionWebUserDto.setNickName(userInfo.getNickName());
        if(ArrayUtils.contains(appConfig.getAdminEmails().split(","),email)){
            sessionWebUserDto.setAdmin(true);
        }else {
            sessionWebUserDto.setAdmin(false);
        }
        UserSpaceDto userSpaceDto = new UserSpaceDto();
        Long useSpace  = fileInfoMapper.selectUseSpaceByUserId(userInfo.getUserId());
        userSpaceDto.setUseSpace(useSpace);
        userSpaceDto.setTotalSpace(userInfo.getTotalSpace());
//        TODO 总空间可能随时更改，所以需要实时查询,并保存到redis
        redisComponent.saveUserSpaceUser(userInfo.getUserId(),userSpaceDto);
        return sessionWebUserDto;
    }

    @Override
    public void resetPwd(String email, String password, String emailCode) {
        UserInfo userInfo = userInfoMapper.selectByEmailUserInfo(email);
        if(userInfo == null){
            throw new RuntimeException("邮箱不存在!");
        }
        emailCodeService.checkEmailCode(email, emailCode);
        UserInfo userInfo1 = new UserInfo();
        userInfo1.setPassword(StringTools.encodeByMD5(password));
        userInfoMapper.updateByEmailUserInfo(userInfo1, email);
    }

    @Override
    public void updateUserInfoByUserId(UserInfo userInfo, String userId) {
        userInfoMapper.updateByUserId(userInfo, userId);
    }

    @Override
    public PaginationResultVO findListByParam(UserInfoQuery query) {
        int count = userInfoMapper.selectCount(query);
        int pageSize = query.getPageSize() == null ? PageSize.SIZE15.getSize() : query.getPageSize();

        SimplePage page = new SimplePage(query.getPageNo(), count, pageSize);
        query.setSimplePage(page);
        List<UserInfo> list = userInfoMapper.selectList(query);
        PaginationResultVO<UserInfo> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
        return result;
    }

    @Override
    @Transactional
    public void updateUserStatus(String userId, Integer status) {
        UserInfo userInfo = new UserInfo();
        userInfo.setStatus(status);
        if(UserStatusEnum.DISABLE.getStatus().equals(status)){
            userInfo.setTotalSpace(0L);
            fileInfoMapper.deleteFileByUserId(userId);
        }
        userInfoMapper.updateByUserId(userInfo, userId);
    }

    @Override
    public void changeUserSpace(String userId, Integer changeSpace) {
        Long space = changeSpace * Constants.MB;
        userInfoMapper.updateUserSpace(userId, null, space);
        redisComponent.resetUserSpaceUser(userId);
    }

    @Override
    public UserInfo getUserInfoByUserId(String userId) {
        UserInfo userInfo = userInfoMapper.selectById(userId);
        return userInfo;
    }

    /**
     * 通过code拿accessToken,
     * 通过accessToken拿openId，
     * 通过openId拿qq用户信息
     * @param code
     * @return
     */
    @Override
    public SessionWebUserDto qqLogin(String code) {
        logger.info("qqLogin:{}",code);
//        第一步 通过回调code 获取accesstoken
        String accessToken = getQQAccessToken(code);
        logger.info("accessToken:{}",accessToken);
//        第二步 通过accesstoken 获取openId
        String qqOpenId = getQQOpenId(accessToken);
        logger.info("qqOpenId:{}",qqOpenId);
        UserInfo userInfo=userInfoMapper.selectByQqOpenId(qqOpenId);
        String avatar = null;
        if (null == userInfo) {
//            如果用户为空，则自动注册
            QQInfoDto qqInfo = getQQUserInfo(accessToken, qqOpenId);
            userInfo = new UserInfo();

            String nickName = qqInfo.getNickname();
            nickName = nickName.length() > Constants.LENGTH_150 ? nickName.substring(0, 150) : nickName;
            avatar = StringTools.isEmpty(qqInfo.getFigureurl_qq_2()) ? qqInfo.getFigureurl_qq_1() : qqInfo.getFigureurl_qq_2();
            Date curDate = new Date();

            //上传头像到本地
            userInfo.setQqOpenId(qqOpenId);
            userInfo.setJoinTime(curDate);
            userInfo.setNickName(nickName);
            userInfo.setQqAvatar(avatar);
            userInfo.setUserId(StringTools.getRandomString(Constants.LENGTH_10));
            userInfo.setLastLoginTime(curDate);
            userInfo.setStatus(UserStatusEnum.ENABLE.getStatus());
            userInfo.setUseSpace(0L);
            userInfo.setTotalSpace(redisComponent.getSysSettings().getUserInitUseSpace() * Constants.MB);
            this.userInfoMapper.insert(userInfo);
            userInfo = userInfoMapper.selectByQqOpenId(qqOpenId);
        } else {
//            如果存在就更新qq用户信息
            UserInfo updateInfo = new UserInfo();
            updateInfo.setLastLoginTime(new Date());
            avatar = userInfo.getQqAvatar();
            this.userInfoMapper.updateByQqOpenId(updateInfo, qqOpenId);
        }
        if (UserStatusEnum.DISABLE.getStatus().equals(userInfo.getStatus())) {
            throw new BusinessException("账号被禁用无法登录");
        }
        SessionWebUserDto sessionWebUserDto = new SessionWebUserDto();
        sessionWebUserDto.setUserId(userInfo.getUserId());
        sessionWebUserDto.setNickName(userInfo.getNickName());
        sessionWebUserDto.setAvatar(avatar);
        if (ArrayUtils.contains(appConfig.getAdminEmails().split(","), userInfo.getEmail() == null ? "" : userInfo.getEmail())) {
            sessionWebUserDto.setAdmin(true);
        } else {
            sessionWebUserDto.setAdmin(false);
        }

        UserSpaceDto userSpaceDto = new UserSpaceDto();
        userSpaceDto.setUseSpace(fileInfoMapper.selectUseSpaceByUserId(userInfo.getUserId()));
        userSpaceDto.setTotalSpace(userInfo.getTotalSpace());
        redisComponent.saveUserSpaceUser(userInfo.getUserId(), userSpaceDto);
        return sessionWebUserDto;
    }

    private String getQQAccessToken(String code) {
        /**
         * 返回结果是字符串
         * access_token=****&expires_in=7776000&refresh_token=****
         * 返回错误
         * callback({UcWebConstants.VIEW_OBJ_RESULT_KEY:111,error_description:"error msg"})
         */
        String accessToken = null;
        String url = null;
        try {
//            拼接参数qq登录accessToken地址，
            url = String.format(appConfig.getQqUrlAccessToken(),
                    appConfig.getQqAppId(),
                    appConfig.getQqAppKey(), code,
                    URLEncoder.encode(appConfig.getQqUrlRedirect(), "utf-8"));
        } catch (UnsupportedEncodingException e) {
            logger.error("获取qqToken失败:{}", e.getMessage());
        }
//        将拼接的accessToken地址，进行请求发生,返回的结果为tokenResult
        String tokenResult = OKHttpUtils.getRequest(url);
        if (tokenResult == null || tokenResult.indexOf(Constants.VIEW_OBJ_RESULT_KEY) != -1) {
            logger.error("获取qqToken失败:{}", tokenResult);
            throw new BusinessException("获取qqToken失败");
        }
        String[] params = tokenResult.split("&");
        if (params != null && params.length > 0) {
            for (String p : params) {
                if (p.indexOf("access_token") != -1) {
                    accessToken = p.split("=")[1];
                    break;
                }
            }
        }
        return accessToken;
    }


    private String getQQOpenId(String accessToken) throws BusinessException {
        // 获取openId
        String url = String.format(appConfig.getQqUrlOpenId(), accessToken);
        String openIDResult = OKHttpUtils.getRequest(url);
        String tmpJson = this.getQQResp(openIDResult);
        if (tmpJson == null) {
            logger.error("调qq接口获取openID失败:tmpJson{}", tmpJson);
            throw new BusinessException("调qq接口获取openID失败");
        }
        Map jsonData = JsonUtils.convertJson2Obj(tmpJson, Map.class);
        if (jsonData == null || jsonData.containsKey(Constants.VIEW_OBJ_RESULT_KEY)) {
            logger.error("调qq接口获取openID失败:{}", jsonData);
            throw new BusinessException("调qq接口获取openID失败");
        }
        return String.valueOf(jsonData.get("openid"));
    }

    private QQInfoDto getQQUserInfo(String accessToken, String qqOpenId) throws BusinessException {
        String url = String.format(appConfig.getQqUrlUserInfo(), accessToken, appConfig.getQqAppId(), qqOpenId);
        String response = OKHttpUtils.getRequest(url);
        if (StringUtils.isNotBlank(response)) {
            QQInfoDto qqInfo = JsonUtils.convertJson2Obj(response, QQInfoDto.class);
            if (qqInfo.getRet() != 0) {
                logger.error("qqInfo:{}", response);
                throw new BusinessException("调qq接口获取用户信息异常");
            }
            return qqInfo;
        }
        throw new BusinessException("调qq接口获取用户信息异常");
    }


    /**
     * 获取qq返回的json数据
     * @param result
     * @return
     */
    private String getQQResp(String result) {
        if (StringUtils.isNotBlank(result)) {
            int pos = result.indexOf("callback");
            if (pos != -1) {
                int start = result.indexOf("(");
                int end = result.lastIndexOf(")");
                String jsonStr = result.substring(start + 1, end - 1);
                return jsonStr;
            }
        }
        return null;
    }


}
